package com.yanjr.common.persistence;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.Transient;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import com.yanjr.common.Constants;
import com.yanjr.common.persistence.Annotation.Column;
import com.yanjr.common.persistence.Annotation.Table;
import com.yanjr.common.persistence.dialect.db.MySQLDialect;
import com.yanjr.common.utils.CamelCaseUtils;
import com.yanjr.common.utils.Reflections;
import com.yanjr.common.utils.StringUtils;

/**
 * @ClassName: BaseDao 
 * @Description: 这里用一句话描述这个类的作用)
 * @author yanjr big123@126.com 
 * @date 2016年2月24日 下午4:45:15
 */
public class BaseDaoImpl<T> implements BaseDao<T> {
	
	/** 设置一些操作的常量 */  
    public static final String SQL_INSERT = "insert";  
    public static final String SQL_UPDATE = "update";  
    public static final String SQL_DELETE = "delete";
    
    public static final String EMPTY = "";
    
    
	/**
	 * JdbcTemplate
	 */
	@Autowired
	public JdbcTemplate jdbcTemplate;
	
	/**
	 * 实体类类型(由构造方法自动赋值)
	 */
	protected Class<T> entityClass;
	
	/**
	 * 构造方法，根据实例类自动获取实体类类型
	 */
	public BaseDaoImpl() {
		entityClass = Reflections.getClassGenricType(getClass());
	}
	
	/**
	 * @Description 通过id获取实体)
	 * @param id
	 * @return T
	 * @date 2016年2月26日下午2:08:16
	 * @author yanjr big123@126.com
	 */
	@Override
	public T get(Serializable id) {
        String sql = "SELECT * FROM " + entityClass.getAnnotation(Table.class).value() + " WHERE del_flag=? and id=?";
        RowMapper<T> rowMapper = BeanPropertyRowMapper.newInstance(entityClass);
        return jdbcTemplate.query(sql, rowMapper, Constants.DEL_FLAG_NORMAL, id).get(0);
    }
	
	@Override
	public T findOne(String sql, Object[] params) {
        RowMapper<T> rowMapper = BeanPropertyRowMapper.newInstance(entityClass);
        return jdbcTemplate.query(sql, rowMapper, params).get(0);
	}
	
	/**
	 * @Description 根据参数查询相关实体
	 * @param sql
	 * @param params 数组参数
	 * @param rowMapper 
	 * @return List<T>
	 * @date 2016年2月25日上午8:11:47
	 * @author yanjr big123@126.com
	 */
	@Override
	public List<T> findList(String sql, Object[] params){
		RowMapper<T> rowMapper = BeanPropertyRowMapper.newInstance(entityClass);
		return jdbcTemplate.query(sql, params, rowMapper);
	}
	
	@Override
	public List<T> findList(String sql) {
		RowMapper<T> rowMapper = BeanPropertyRowMapper.newInstance(entityClass);
		return jdbcTemplate.query(sql, rowMapper);
	}
	
	@Override
	public Page<T> findPage(Page<T> page, String sql, Object... params) {
		RowMapper<T> rowMapper = BeanPropertyRowMapper.newInstance(entityClass);
		String countSQL = this.makeCountSql(sql);
		int count = 0;
		List<T> list = new ArrayList<>();
		/* 计算数据下标值 */
		int offset = page.getPageNo() == 1? 0 : ((page.getPageNo()-1) * page.getPageSize())-1;
		/* sql语句添加分页 */
		sql = MySQLDialect.getLimitString(sql, offset, Integer.toString(offset), Integer.toString(page.getPageSize()));
		if(params != null && params.length > 0) {
			count = jdbcTemplate.queryForObject(countSQL, params, Integer.class);
			list = jdbcTemplate.query(sql, params, rowMapper);
		} else {
			count = jdbcTemplate.queryForObject(countSQL, Integer.class);
			System.out.println("countSQL=" + countSQL);
			list = jdbcTemplate.query(sql, rowMapper);
			System.out.println("sql=" + sql);
		}
		
		page.setCount(count);
		page.setList(list);
		return page;
	}
	
	/**
	 * @Description 查询所有实体
	 * @return List<T>
	 * @date 2016年2月26日下午2:09:00
	 * @author yanjr big123@126.com
	 */
	@Override
	public List<T> findAll() {
        String sql = "SELECT * FROM " + entityClass.getAnnotation(Table.class).value() 
        		+ " where del_flag='" + Constants.DEL_FLAG_NORMAL + "' ";
        RowMapper<T> rowMapper = BeanPropertyRowMapper.newInstance(entityClass);
        return jdbcTemplate.query(sql, rowMapper);
    }
	
	
	/**
	 * @Description 保存实体
	 * @param entity
	 * @date 2016年2月26日下午2:17:37
	 * @author yanjr big123@126.com
	 */
	@Override
	public int save(T entity) {
		Map<String, Object> pojo = this.getEntityInfo(entity);
		Object[][] buf = this.getPropertyAndValue(pojo);
		System.out.println("buf[0]=" + Arrays.toString(buf[0]));
		System.out.println("buf[1]=" + Arrays.toString(buf[1]));
		String sql = this.makeSql(SQL_INSERT, entity, pojo, buf[0]);
		return jdbcTemplate.update(sql, buf[1]);
	}
	
	/**
	 * 循环保存
	 */
	@Override
	public void save(List<T> entityList) {
		for(T entity : entityList) {
			save(entity);
		}
	}
	
	
	/**
	 * 更新实体
	 */
	@Override
	public int update(T entity) {
		Map<String, Object> pojo = this.getEntityInfo(entity);
		Object[][] buf = this.getColumnAndValueWithoutNull(pojo);
		System.out.println("buf[0]=" + Arrays.toString(buf[0]));
		System.out.println("buf[1]=" + Arrays.toString(buf[1]));
		String sql = this.makeSql(SQL_UPDATE, entity, pojo, buf[0]);
		System.out.println(">>>sql= " + sql);
		return jdbcTemplate.update(sql, buf[1]);
	}
	
	/**
	 * 循环更新实体
	 * 
	 * @param entityList
	 * @author yanjr
	 */
	public void update(List<T> entityList) {
		for(T entity : entityList) {
			this.update(entity);
		}
	}
	
	/**
	 * @Description 删除实体
	 * @param entity
	 * @date 2016年2月26日下午2:18:05
	 * @author yanjr big123@126.com
	 */
	@Override
	public int delete(T entity) {  
		Map<String, Object> pojo = this.getEntityInfo(entity);
		Object[][] buf = this.getPropertyAndValue(pojo);
		return jdbcTemplate.update(this.makeSql(SQL_DELETE, entity, pojo, buf[0]), buf[1]);
	}
	
	/**
	 * @Description 通过id删除实体
	 * @param id
	 * @date 2016年2月26日下午2:18:17
	 * @author yanjr big123@126.com
	 */
	@Override
	public int delete(Serializable id) {  
        String sql = " DELETE FROM " + entityClass.getAnnotation(Table.class).value() + " WHERE id=?";
        return jdbcTemplate.update(sql, id);
    }
	
	@Override
	public int deleteById(Serializable id) {
		String sql = " UPDATE " + entityClass.getAnnotation(Table.class).value() + " set del_flag=? WHERE id=?";
        return jdbcTemplate.update(sql, Constants.DEL_FLAG_DELETE, id);
	}
	
	@Override
	public int deleteById(Serializable id, String likeParentIds) {
		String sql = " UPDATE " + entityClass.getAnnotation(Table.class).value() + " set del_flag='1'" + " WHERE id=? or parent_ids like ?";
        return jdbcTemplate.update(sql, id, likeParentIds);
	}
	
	/**
	 * @Description 删除所有实体
	 * @date 2016年2月26日下午2:18:34
	 * @author yanjr big123@126.com
	 */
	@Override
	public void deleteAll() {  
        String sql = " TRUNCATE TABLE " + entityClass.getAnnotation(Table.class).value();  
        jdbcTemplate.execute(sql);  
    }
	
	/**
	 * @Description 组装SQL
	 * @param sqlFlag 语句类型（insert update delete ...）
	 * @param tName 数据库表名（可为空：为空表示实例名称跟数据库名称一致，如 Entity：User  table：user）
	 * @return String sql语句
	 * @date 2016年2月27日上午9:05:54
	 * @author yanjr big123@126.com
	 */
    private String makeSql(String sqlFlag, T entity, Map<String, Object> pojo, Object[] buf) {
    	String tableName = entity.getClass().getAnnotation(Table.class).value();
        StringBuffer sql = new StringBuffer();
        StringBuilder fields = new StringBuilder("");
		StringBuilder values = new StringBuilder("");
        if (sqlFlag.equals(SQL_INSERT)) {  
            sql.append("INSERT INTO " + tableName);  
            sql.append("(");  
            for (int i = 0, len = buf.length; i < len; i++) {
    			fields.append(CamelCaseUtils.toUnderlineName(buf[i].toString()));
    			values.append("?");
    			if (i < len - 1) {
    				fields.append(",");
    				values.append(",");
    			}
    		}
            sql.append(fields.toString());
            sql.append(") values (");
            sql.append(values.toString());
            sql.append(")");
        } else if (sqlFlag.equals(SQL_UPDATE)) {  
            sql.append("UPDATE " + tableName + " SET ");  
            for (int i = 0, len = buf.length - 1; i < len; i++) {
                String column = CamelCaseUtils.toUnderlineName(buf[i].toString());
//                if (column.equals("id")) { // id 代表主键  
//                    continue;  
//                }  
                sql.append(column).append("=").append("?,");  
            }  
            sql = sql.deleteCharAt(sql.length() - 1);  
            sql.append(" WHERE id=?");  
        } else if (sqlFlag.equals(SQL_DELETE)) {  
            sql.append("DELETE FROM " + tableName + " WHERE id=?");  
        }  
        System.out.println("SQL=" + sql.toString());  
        return sql.toString();  
    }
    
    public String makeCountSql(String sql) {
    	sql = sql.toUpperCase();
    	String countSql = "SELECT COUNT(*) " + sql.substring(sql.indexOf("FROM"));
    	return countSql;
    }
    
    public String makeFindPageSql(String tName, T entity) {
        StringBuffer sql = new StringBuffer();
        /* 判断表名是传值还是自动获取 */
        String tableName = StringUtils.isEmpty(tName) ? CamelCaseUtils.toUnderlineName(entityClass.getSimpleName()) : tName;
        Field[] fields = entityClass.getDeclaredFields();
        try {
			sql.append(" SELECT * FROM " + tableName);  
			sql.append(" WHERE ");  
			for (int i = 0; fields != null && i < fields.length; i++) {  
			    fields[i].setAccessible(true); // 暴力反射  
			    if(null != fields[i].getAnnotation(Transient.class)) {
                	continue;
                }
			    String column = CamelCaseUtils.toUnderlineName(fields[i].getName());
			    // 第一个参数是传进去的方法名称，第二个参数是 传进去的类型；
				Method m = entity.getClass().getMethod("get" + column,fields[i].getType());
				// 第二个参数是传给set方法数据；如果是get方法可以不写
				if(m.invoke(entity) != null) {
					sql.append(column).append("=? ADN");
				}
			}
			sql = sql.deleteCharAt(sql.length() - 3);
			
		} catch (Exception e) {
			e.printStackTrace();
		}
        System.out.println("PageSQL=" + sql);  
        return sql.toString();  
    }
    
	/**
	 * @Description 设置参数
	 * @param entity
	 * @param sqlFlag
	 * @return
	 * @date 2016年2月27日下午2:59:22
	 * @author yanjr big123@126.com
	 */
    @SuppressWarnings("unused")
	private Object[] setArgs(T entity, String sqlFlag) {  
        Field[] fields = entityClass.getDeclaredFields();
        
        if (sqlFlag.equals(SQL_INSERT)) {
        	List<Object> list = new ArrayList<>();
            for(int i = 0, len = fields.length; i < len; i++) {
                try {
                    fields[i].setAccessible(true); // 暴力反射  
                    Object object = fields[i].get(entity);
                    if(object != null) {
                		list.add(object);
                	}
                } catch (Exception e) {
                    e.printStackTrace();  
                }  
            }
           
            return list.toArray();  
        } else if (sqlFlag.equals(SQL_UPDATE)) {  
            Object[] tempArr = new Object[fields.length];  
            for (int i = 0; tempArr != null && i < tempArr.length; i++) {  
                try {  
                    fields[i].setAccessible(true); // 暴力反射  
                    tempArr[i] = fields[i].get(entity);
                } catch (Exception e) {  
                    e.printStackTrace();  
                }  
            }
            
            //去除不在数据库表中对象
            List<Object> list = new ArrayList<>();
            for(int i = 0; i < tempArr.length; i++) {
            	if(tempArr[i] != null) {
            		list.add(tempArr[i]);
            	}
            }
            Object[] arr = list.toArray();  
            
            Object[] args = new Object[arr.length];  
            System.arraycopy(arr, 1, args, 0, arr.length - 1); // 数组拷贝  
            args[args.length - 1] = tempArr[0];  
            return args;  
        } else if (sqlFlag.equals(SQL_DELETE)) {  
            Object[] args = new Object[1]; // 长度是1  
            fields[0].setAccessible(true); // 暴力反射 
            try {  
                args[0] = fields[0].get(entity);//可能必须把id放在第一位置
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
            return args;  
        }  
        return null;  
    }
    
    public Object[] setFindPageArgs(T entity) {  
        Field[] fields = entityClass.getDeclaredFields();  
        List<Object> list = new ArrayList<Object>();
        for (int i = 0, len = fields.length; i < len; i++) {  
            try {  
                fields[i].setAccessible(true); // 暴力反射  
                Object object = fields[i].get(entity);
                if(object != null) {
                	list.add(object);
                }
                
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
        }  
        return list.toArray(new Object[list.size()]);
    }
    
    
    // 设置参数类型（写的不全，只是一些常用的)
    @SuppressWarnings("unused")
    private int[] setArgTypes(T entity, String sqlFlag) {
        Field[] fields = entityClass.getDeclaredFields();
        if (sqlFlag.equals(SQL_INSERT)) {  
            List<Object> list = new ArrayList<>();
            try{  
                for(int i = 0, len = fields.length; i < len; i++) {
                    fields[i].setAccessible(true); // 暴力反射 
                    int index = getFiledType(fields[i]);
                    if(index != -1) {
                		list.add(index);
                	}
                }
            } catch (Exception e) {  
                e.printStackTrace();  
            }
            
            int[] result = new int[list.size()];  
            for(int i = 0, len = list.size(); i < len; i++) {
            	result[i] = Integer.parseInt(list.get(i).toString());
            }
            return result;
        } else if (sqlFlag.equals(SQL_UPDATE)) {  
            List<Object> list = new ArrayList<>();
            try {  
                for (int i = 0, len = fields.length; i < len; i++) {  
                    fields[i].setAccessible(true); // 暴力反射  
                    int index = getFiledType(fields[i]);
                    if(index != -1) {
                		list.add(index);
                	}
                }
                
                int[] result = new int[list.size()];  
                for(int i = 0, len = list.size(); i < len; i++) {
                	result[i] = Integer.parseInt(list.get(i).toString());
                }
                
                int[] argTypes = new int[result.length];  
                System.arraycopy(result, 1, argTypes, 0, result.length - 1); // 数组拷贝  
                argTypes[argTypes.length - 1] = result[0];
                
                return argTypes;
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
        } else if (sqlFlag.equals(SQL_DELETE)) {  
            int[] argTypes = new int[1]; // 长度是1  
            try {  
                fields[0].setAccessible(true); // 暴力反射  
                argTypes[0] = getFiledType(fields[0]); 
  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
            return argTypes;  
        }  
        return null;  
    }

//    private int[] setArgTypes(T entity, String sqlFlag) {
//    	
//    	
//    }

    
    /**
     * @Description 判断字段的类型，输出数据库字段类型
     * @param field
     * @return int
     * @date 2016年2月27日下午2:58:06
     * @author yanjr big123@126.com
     */
	private int getFiledType(Field field) {
		if("java.lang.String".equals(field.getType().getName())) {
		    return Types.VARCHAR;
		} else if ("java.lang.Double".equals(field.getType().getName())) {
			return Types.DOUBLE;
		} else if ("java.util.Float".equals(field.getType().getName())) {
			return Types.FLOAT;
		} else if ("java.lang.Integer".equals(field.getType().getName())) {
			return Types.INTEGER;
		} else if ("java.util.Date".equals(field.getType().getName())) {
//			return Types.DATE;
			return Types.TIMESTAMP;
		} else if ("java.math.BigDecimal".equals(field.getType().getName())) {
			return Types.DECIMAL;
		}
		
		return -1; 
	}

	@Override
	public int update(String sql, Object... params) {
		return jdbcTemplate.update(sql, params);
	}

//----------------------
	
	/**
	 * 反射得到实例对象的信息跟值
	 * 
	 * @param entity
	 * @return
	 */
	public Map<String, Object> getEntityInfo(T entity) {
		Field[] fs = entity.getClass().getDeclaredFields();
		Map<String, Object> valuesMap = new HashMap<String, Object>();
		for (int i = 0; i < fs.length; i++) {
			String name = fs[i].getName();
			if ("serialVersionUID".equals(name) || "subTableFlag".equals(name)) {
				continue;
			}
			if (fs[i].isAnnotationPresent(Column.class)) {
				try {
					Method m = entity.getClass().getMethod("get" + getFirstUpperString(name));
					Object buf = m.invoke(entity);
					valuesMap.put(name, buf);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		return valuesMap;
	}
	
	/**
	 * 获取列和值
	 * 
	 * @param data
	 * @return
	 */
	public Object[][] getPropertyAndValue(Map<String, Object> data) {
		Object[][] result = new Object[2][];
		Object[] columns = new Object[data.size()];
		Object[] values = new Object[data.size()];
		Set<String> keys = data.keySet();
		int i = 0;
		for (String key : keys) {
			columns[i] = key;
			values[i] = data.get(key);
			i++;
		}
		result[0] = columns;
		result[1] = values;
		return result;
	}
	
	/**
	 * 获取列和值，排除null值
	 * 	将id放置最后一个参数
	 * 
	 * @param data
	 * @return
	 */
	public Object[][] getColumnAndValueWithoutNull(Map<String, Object> data) {
		Object[][] result = new Object[2][];
		
		Map<String, Object> map = new HashMap<String, Object>();
		Object id = null;
		Set<String> keys = data.keySet();
		
		for(String key : keys) {
			Object o = data.get(key);
			if("id".equals(key)) {
				id = o;
				continue;
			}
			if(o != null) {
				map.put(key, o);
			}
		}
		
		int len = map.size() + 1;
		Object[] columns = new Object[len];
		Object[] values = new Object[len];
		
		Set<String> keys1 = map.keySet();
		int i = 0;
		for (String key : keys1) {
			columns[i] = key;
			values[i] = data.get(key);
			i++;
		}
		columns[i] = "id";
		values[i] = id;
		
		result[0] = columns;
		result[1] = values;
		return result;
	}
	
	
	/**
	 * 首字母大写
	 * 
	 * @param s
	 * @return
	 */
	public static String getFirstUpperString(String s) {
		if (s == null || s.length() < 1) {
			return EMPTY;
		}
		String s1 = s.substring(0, 1);
		String s2 = s.substring(1);
		return s1.toUpperCase() + s2;
	}

	

	
	
}
